﻿using System;
using System.Management;
using System.Security.Cryptography;
using System.IO;
using System.Collections;
using System.Runtime;
using System.Runtime.InteropServices;
/// <summary>
/// This class is designed by bongvd for the Encode password , files
///
/// 
namespace eTMS.Commons
{

    public class Security
    {
        public static string RemoveQuoteString(string strvalidate)
        {
            return strvalidate.Replace("'", "''");
        }
        public static string GenRandomPasword(int len)
        {
            string str = "";
            string strresult = "";
            for (int i = 0; i < 26; i++)
            {
                str += ((char)(65 + i)).ToString();
            }
            for (int i = 0; i < 26; i++)
            {
                str += ((char)(97 + i)).ToString();
            }
            for (int i = 0; i < 10; i++)
            {
                str += i.ToString();
            }
            Random rd = new Random();
            while (len > 0)
            {
                int i = rd.Next(str.Length);
                strresult += str[i].ToString();
                len--;
                System.Threading.Thread.Sleep(1);
            }
            return strresult;
        }
        //this function Encode Password by md5 
        //password encoded must be the same with server encode pass
        public static string EncriptPassWord(string password)
        {
            if (password.Trim() == "") return "";
            byte[] buff = System.Text.ASCIIEncoding.ASCII.GetBytes(password);
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            buff = md5.ComputeHash(buff);
            string strResult = "";
            for (int i = 0; i < buff.Length; i++)
            {
                strResult += string.Format("{0:x2}", buff[i]);
            }
            return strResult;
        }

        /// <summary>
        /// Tag to make sure this file is readable/decryptable by this class
        /// </summary>
        private const ulong FC_TAG = 0xFC010203040506CF;

        /// <summary>
        /// The amount of bytes to read from the file
        /// </summary>
        private const int BUFFER_SIZE = 128 * 1024;

        /// <summary>
        /// Checks to see if two byte array are equal
        /// </summary>
        /// <param name="b1">the first byte array</param>
        /// <param name="b2">the second byte array</param>
        /// <returns>true if b1.Length == b2.Length and each byte in b1 is
        /// equal to the corresponding byte in b2</returns>
        private static bool CheckByteArrays(byte[] b1, byte[] b2)
        {
            if (b1.Length == b2.Length)
            {
                for (int i = 0; i < b1.Length; ++i)
                {
                    if (b1[i] != b2[i])
                        return false;
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// Creates a Rijndael SymmetricAlgorithm for use in EncryptFile and DecryptFile
        /// </summary>
        /// <param name="password">the string to use as the password</param>
        /// <param name="salt">the salt to use with the password</param>
        /// <returns>A SymmetricAlgorithm for encrypting/decrypting with Rijndael</returns>
        private static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt, "SHA256", 1000);
            SymmetricAlgorithm sma = Rijndael.Create();
            sma.KeySize = 256;
            sma.Key = pdb.GetBytes(32);
            sma.Padding = PaddingMode.PKCS7;
            return sma;
        }

        /// <summary>
        /// Crypto Random number generator for use in EncryptFile
        /// </summary>
        private static RandomNumberGenerator rand = new RNGCryptoServiceProvider();

        /// <summary>
        /// Generates a specified amount of random bytes
        /// </summary>
        /// <param name="count">the number of bytes to return</param>
        /// <returns>a byte array of count size filled with random bytes</returns>
        private static byte[] GenerateRandomBytes(int count)
        {
            byte[] bytes = new byte[count];
            rand.GetBytes(bytes);
            return bytes;
        }
        /// <summary>
        /// This takes an input file and encrypts it into the output file
        /// </summary>
        /// <param name="inFile">the file to encrypt</param>
        /// <param name="outFile">the file to write the encrypted data to</param>
        /// <param name="password">the password for use as the key</param>
        /// <param name="callback">the method to call to notify of progress</param>
        public static void EncryptFile(string inFile, string outFile, string password)
        {
            if (System.IO.File.Exists(outFile))
            {
                System.IO.File.Delete(outFile);
            }
            using (FileStream fin = File.OpenRead(inFile),
                        fout = File.OpenWrite(outFile))
            {
                long lSize = fin.Length; // the size of the input file for storing
                int size = (int)lSize;  // the size of the input file for progress
                byte[] bytes = new byte[BUFFER_SIZE]; // the buffer
                int read = -1; // the amount of bytes read from the input file
                int value = 0; // the amount overall read from the input file for progress

                // generate IV and Salt
                byte[] IV = GenerateRandomBytes(16);
                byte[] salt = GenerateRandomBytes(16);

                // create the crypting object
                SymmetricAlgorithm sma = CreateRijndael(password, salt);
                sma.IV = IV;

                // write the IV and salt to the beginning of the file
                fout.Write(IV, 0, IV.Length);
                fout.Write(salt, 0, salt.Length);

                // create the hashing and crypto streams
                HashAlgorithm hasher = SHA256.Create();
                using (CryptoStream cout = new CryptoStream(fout, sma.CreateEncryptor(), CryptoStreamMode.Write),
                            chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                {
                    // write the size of the file to the output file
                    BinaryWriter bw = new BinaryWriter(cout);
                    bw.Write(lSize);

                    // write the file cryptor tag to the file
                    bw.Write(FC_TAG);

                    // read and the write the bytes to the crypto stream in BUFFER_SIZEd chunks
                    while ((read = fin.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        cout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        //callback(0,size,value);
                    }
                    // flush and close the hashing object
                    chash.Flush();
                    chash.Close();

                    // read the hash
                    byte[] hash = hasher.Hash;

                    // write the hash to the end of the file
                    cout.Write(hash, 0, hash.Length);

                    // flush and close the cryptostream
                    cout.Flush();
                    cout.Close();
                }
            }
        }

        /// <summary>
        /// takes an input file and decrypts it to the output file
        /// </summary>
        /// <param name="inFile">the file to decrypt</param>
        /// <param name="outFile">the to write the decrypted data to</param>
        /// <param name="password">the password used as the key</param>
        /// <param name="callback">the method to call to notify of progress</param>
        public static void DecryptFile(string inFile, string outFile, string password)
        {
            // NOTE:  The encrypting algo was so much easier...
            // create and open the file streams
            if (System.IO.File.Exists(outFile))
            {
                System.IO.File.Delete(outFile);
            }
            using (FileStream fin = File.OpenRead(inFile),
                        fout = File.OpenWrite(outFile))
            {
                int size = (int)fin.Length; // the size of the file for progress notification
                byte[] bytes = new byte[BUFFER_SIZE]; // byte buffer
                int read = -1; // the amount of bytes read from the stream
                int value = 0;
                int outValue = 0; // the amount of bytes written out

                // read off the IV and Salt
                byte[] IV = new byte[16];
                fin.Read(IV, 0, 16);
                byte[] salt = new byte[16];
                fin.Read(salt, 0, 16);

                // create the crypting stream
                SymmetricAlgorithm sma = CreateRijndael(password, salt);
                sma.IV = IV;

                value = 32; // the value for the progress
                long lSize = -1; // the size stored in the input stream

                // create the hashing object, so that we can verify the file
                HashAlgorithm hasher = SHA256.Create();

                // create the cryptostreams that will process the file
                using (CryptoStream cin = new CryptoStream(fin, sma.CreateDecryptor(), CryptoStreamMode.Read),
                            chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                {
                    // read size from file
                    BinaryReader br = new BinaryReader(cin);
                    lSize = br.ReadInt64();
                    ulong tag = br.ReadUInt64();

                    if (FC_TAG != tag)
                        throw new CryptoHelpException("File Corrupted!");

                    //determine number of reads to process on the file
                    long numReads = lSize / BUFFER_SIZE;

                    // determine what is left of the file, after numReads
                    long slack = (long)lSize % BUFFER_SIZE;

                    // read the buffer_sized chunks
                    for (int i = 0; i < numReads; ++i)
                    {
                        read = cin.Read(bytes, 0, bytes.Length);
                        fout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        outValue += read;
                        //callback(0,size,value);
                    }
                    // now read the slack
                    if (slack > 0)
                    {
                        read = cin.Read(bytes, 0, (int)slack);
                        fout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        outValue += read;
                        //callback(0,size,value);
                    }
                    // flush and close the hashing stream
                    chash.Flush();
                    chash.Close();

                    // flush and close the output file
                    fout.Flush();
                    fout.Close();

                    // read the current hash value
                    byte[] curHash = hasher.Hash;

                    // get and compare the current and old hash values
                    byte[] oldHash = new byte[hasher.HashSize / 8];
                    read = cin.Read(oldHash, 0, oldHash.Length);
                    if ((oldHash.Length != read) || (!CheckByteArrays(oldHash, curHash)))
                        throw new CryptoHelpException("File Corrupted!");
                }

                // make sure the written and stored size are equal
                if (outValue != lSize)
                    throw new CryptoHelpException("File Sizes don't match!");
            }
        }


        private static Byte[] MaskPasword(string password)
        {
            Byte[] buff = new Byte[password.Length];
            for (int i = 0; i < password.Length; i++)
            {
                if (Char.IsDigit(password[i]))
                {
                    buff[i] = (Byte)((int)password[i] - 0x30);
                }
                else
                {
                    buff[i] = (Byte)((int)password[i] % 10);
                }
            }
            for (int i = 0; i < password.Length; i++)
            {
                if (buff[i] <= 0)
                    buff[i] = 1;
            }
            return buff;
        }
        public static String EncryptString(String strin, String Password)
        {
            Char[] buff = strin.ToCharArray();
            EncryptChars(buff, Password);
            return new String(buff);
        }
        private static void InvertChars(Char[] buff, int start, int len)
        {
            if (len + start > buff.Length)
            {
                len = buff.Length - start;
            }
            Char[] tmp = new Char[len];
            int count = 0;
            for (int i = len + start - 1; i >= start; i--)
            {
                tmp[count] = buff[i];
                count++;
            }
            count = 0;
            for (int i = start; i < start + len; i++)
            {
                buff[i] = tmp[count];
                count++;
            }

        }
        public static void EncryptChars(Char[] buff, string password)
        {
            if (password == "") return;
            Byte[] mask = MaskPasword(password);
            int count = 0;
            int start = 0;
            while (start < buff.Length)
            {
                if (mask[count] > 1)
                {
                    InvertChars(buff, start, mask[count]);
                }
                start += mask[count];
                count++;
                if (count >= mask.Length)
                    count = 0;
            }

        }
        public static String DecryptString(String strint, String Password)
        {
            Char[] buff = strint.ToCharArray();
            EncryptChars(buff, Password);
            return new String(buff);
        }



        public static string CreateLengthFixString(int iRoot, byte bLength)
        {
            return (iRoot + 1000000000).ToString().Substring(10 - bLength);
        }

        // Ham lay ngau nhien cac vi tri trong mot mang co n phan tu
        public static ArrayList RandomArray(ArrayList arrSource)
        {
            int n = arrSource.Count;
            ArrayList arrReturn = new ArrayList();
            Random rd = new Random(DateTime.Now.Millisecond);
            while (arrReturn.Count < n)
            {
                int i = rd.Next(n);
                if (!arrReturn.Contains(arrSource[i].ToString()))
                {
                    arrReturn.Add(arrSource[i].ToString());
                }
            }
            return arrReturn;
        }


        public static ArrayList RandomArray(int n)
        {
            ArrayList arrReturn = new ArrayList();
            Random rd = new Random(DateTime.Now.Millisecond);
            while (arrReturn.Count < n)
            {
                int i = rd.Next(n);
                if (!arrReturn.Contains(i.ToString()))
                {
                    arrReturn.Add(i.ToString());
                }
            }
            return arrReturn;
        }

        // Ham quy hoach dong, tim cac phan tu (luu trong arrPosition) cua mang arrContent, sao cho tong cac gia tri bang total cho truoc.
        public static bool Calculation(int total, ArrayList arrContent, ArrayList arrUsed, ref ArrayList arrPosition)
        {
            for (int i = 1; i <= arrContent.Count; i++)
            {
                if (arrUsed.Contains((i - 1).ToString()))
                {
                    continue;
                }
                if (total.ToString() == arrContent[i - 1].ToString())
                {
                    arrPosition.Add((i - 1).ToString());
                    return true;
                }
            }
            for (int i = 1; i <= arrContent.Count; i++)
            {
                if (arrUsed.Contains((i - 1).ToString()))
                {
                    continue;
                }
                ArrayList arrUsed1 = (ArrayList)arrUsed.Clone();
                arrUsed1.Add((i - 1).ToString());
                if (Calculation(total - Convert.ToInt32(arrContent[i - 1].ToString()), arrContent, arrUsed1, ref arrPosition))
                {
                    arrPosition.Add((i - 1).ToString());
                    return true;
                }
            }
            return false;
        }



      

    }
    /// <summary>
    /// This is the exception class that is thrown throughout the Decryption process
    /// </summary>
    public class CryptoHelpException : ApplicationException
    {
        public CryptoHelpException(string msg) : base(msg) { }
    }
    public class LisenceInfo
    {
        public LisenceInfo()
        {

        }
        const char spl = '$';
        private string _UserName = "";
        private string _ProductCode = "";
        private string _Email = "";
        private string _RequestCode = "";
        private string _Organization = "";
        private string _ProductRegisterCode = "";
        private string _RegiterType = "0";
        public string RegiterType
        {
            set
            {
                _RegiterType = value;
            }
            get
            {
                return _RegiterType;
            }
        }
        public string UserName
        {
            set
            {
                _UserName = value;
            }
            get
            {
                return _UserName;
            }
        }
        public string Organization
        {
            set
            {
                _Organization = value;
            }
            get
            {
                return _Organization;
            }
        }
        public string ProductCode
        {
            set
            {
                _ProductCode = value;
            }
            get
            {
                return _ProductCode;
            }
        }
        public string Email
        {
            set
            {
                _Email = value;
            }
            get
            {
                return _Email;
            }
        }
        public string RequestCode
        {
            set
            {
                _RequestCode = value;
            }
            get
            {
                return _RequestCode;
            }
        }
        public string ProductRegisterCode
        {
            set
            {
                _ProductRegisterCode = value;
            }
            get
            {
                return _ProductRegisterCode;
            }
        }
        public override string ToString()
        {
            return _UserName + spl.ToString() + _RequestCode + spl.ToString() + _ProductRegisterCode.ToString() + spl.ToString() + _ProductCode + spl.ToString() + _Email + spl.ToString() + _Organization;
        }
        private static bool _isRegister = false;
        private static int _count=50;
        public static int Count
        {
            get
            {
                return _count;
            }
        }
        public static bool IsRegister
        {
            get
            {
                return _isRegister;
            }
        }
        public static LisenceInfo Parse(string s)
        {
            LisenceInfo lic = new LisenceInfo();
            string[] ss = s.Split(spl);
            if (ss.Length != 6) return null;
            lic.UserName = ss[0];
            lic.RequestCode = ss[1];
            lic.ProductRegisterCode = ss[2];
            lic.ProductCode = ss[3];
            lic.Email = ss[4];
            lic.Organization = ss[5];
            return lic;
        }
        public static void Save(LisenceInfo lic, string FileName)
        {
            if (System.IO.File.Exists(FileName + "_tmp"))
            {
                System.IO.File.Delete(FileName + "_tmp");
            }
            System.IO.StreamWriter strm = System.IO.File.CreateText(FileName + "_tmp");
            strm.Write(lic.ToString());
            strm.Close();
            Security.EncryptFile(FileName + "_tmp", FileName, lic.RequestCode);
            try
            {
                System.IO.File.Delete(FileName + "_tmp");
            }
            catch
            {

            }
        }
        public static LisenceInfo Read(string FileName)
        {
            if (!System.IO.File.Exists(FileName)) return null;
            Security.DecryptFile(FileName, FileName + "_tmp", GetRequestCode());
            System.IO.StreamReader strReader = System.IO.File.OpenText(FileName + "_tmp");
            LisenceInfo lic = LisenceInfo.Parse(strReader.ReadLine());
            strReader.Close();
            try
            {
                System.IO.File.Delete(FileName + "_tmp");
            }
            catch
            {

            }
            return lic;
        }
        private static string[] GetRegKey()
        {
            string st1 = GetRequestCode();
            st1 = st1.Replace("-", "");
            st1 = Security.EncryptString(st1, Commons.Constants.APP_PASSWORD);
            st1 = Security.EncriptPassWord(st1).ToUpper();
            st1 = Security.EncryptString(st1, Commons.Constants.APP_PASSWORD);
            string[] st = new string[4];
            st[0] = st1.Substring(0, 4);
            st[1] = st1.Substring(4, 4);
            st[2] = st1.Substring(8, 4);
            st[3] = st1.Substring(12, 4);
            return st;
        }
        public static string GetRequestCode()
        {
            string st1 = HarkWareInfo.GetHarkWareInfo();
            st1 = Security.EncryptString(st1, Commons.Constants.APP_PASSWORD);
            string[] st = new string[4];
            st[0] = st1.Substring(0, 4);
            st[1] = st1.Substring(4, 4);
            st[2] = st1.Substring(8, 4);
            st[3] = st1.Substring(12, 4);
            return string.Join("-", st);
        }

        public static bool CheckRegister(string FileName)
        {
            //Get register code          
            LisenceInfo lic = new LisenceInfo();
            string reg = string.Join("-", GetRegKey());
            try
            {
                lic = LisenceInfo.Read(FileName);
            }
            catch
            {

            }
            if (lic != null)
            {
                if (lic.ProductRegisterCode.ToUpper() != reg.ToUpper())
                {
                    _isRegister = false;
                }
                else
                {
                    _isRegister = true;
                    Commons.Constants.APP_REGISTER_NAME = lic.UserName;
                    Commons.Constants.APP_REGISTER_ORGANIZATION = lic.Organization;
                    Commons.Constants.APP_REGISTER_EMAIL = lic.Email;
                    Commons.Constants.APP_REGISTER_PRODUCTCODE = lic.ProductCode;
                }
            }
            return _isRegister;
        }
       
    }
    public class HarkWareInfo
    {
        public static string GetHarkWareInfo()
        {
            string key = "Win32_DiskDrive";
            string r = "";
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + key);
            System.Management.ManagementObjectCollection col = searcher.Get();
            bool br = false;
            foreach (ManagementObject share in col)
            {

                if (share != null)
                {
                    foreach (PropertyData PC in share.Properties)
                    {
                        if (PC.Name.ToLower() == "serialnumber")
                        {
                            r = PC.Value.ToString();
                            br = true;
                            break;
                        }
                    }
                    if (br) break;
                }
            }
            if (r == "")
            {
                r = Security.EncriptPassWord(System.Environment.MachineName + "EDTECH-VN");
            }
            return r;
        }
    }
   
}